package com.eobard.utils;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.StreamUtils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Author: Eobard Gu
 * @Date: 2025-07-25
 * @Description: 文件操作工具类
 */
@Slf4j
public class FileUtils {
    //音频文件
    public static final List<String> AUDIO = Arrays.asList(".mp3");
    //图片文件
    public static final List<String> IMAGE = Arrays.asList(".jpg", ".png");
    //模型文件
    public static final List<String> MODEL = Arrays.asList(MnnUtils.MNN_EXT);

    //资源文件夹下面的所有文件类型
    private static final List<String> RESOURCE_FILE_EXT = Arrays.asList(".xml", ".png", ".mp3", ".jpg");

    private FileUtils() {
    }

    /**
     * 获取path中所有type的文件
     *
     * @param path
     * @param type
     */
    public static List<String> loopTypeExtFiles(String path, String type) {
        List<String> result = new ArrayList<>();
        FileUtil.loopFiles(path, pathname -> pathname.getName().endsWith(type)).forEach(file -> {
            result.add(file.toString());
        });
        return result;
    }


    /**
     * 获取resourceRoot中所有的xml文件
     *
     * @param allResourcesMap
     * @param resourceRoot
     */
    public static void loopXMLFiles(Map<String, File> allResourcesMap, String resourceRoot) {
        FileUtil.loopFiles(resourceRoot, pathname -> pathname.getName().endsWith(".xml")).forEach(file -> {
            allResourcesMap.put(file.toString(), file);
        });
    }

    /**
     * 获取sourceRoot中所有的java文件并放入fileMap中
     *
     * @param fileMap
     * @param sourceRoot
     */
    public static void loopFiles(Map<String, CompilationUnit> fileMap, String sourceRoot) {
        FileUtil.loopFiles(sourceRoot, pathname -> pathname.getName().endsWith(".java")).forEach(p -> {
            try {
                fileMap.put(p.toString(), StaticJavaParser.parse(p));
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }
        });
    }

    /**
     * 获取sourceRoot中的所有Java文件,并按照action进行遍历
     *
     * @param sourceRoot
     * @param action
     */
    public static void loopFiles(String sourceRoot, Consumer<? super Path> action) {
        try {
            Files.walk(Paths.get(sourceRoot))
                    .filter(p -> p.toString().endsWith(".java"))
                    .forEach(action);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 保存混淆后的代码到文件中
     *
     * @param fileMap
     */
    public static void saveObfuscatedFiles(Map<String, CompilationUnit> fileMap) {
        fileMap.forEach((path, cu) -> {
            try {
                Files.writeString(Paths.get(path), cu.toString());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
    }


    /**
     * 保存混淆后的代码到文件中
     *
     * @param fileMap
     * @param needCreateDir:如果fileMap中有新的包路径,是否需要创建不存在的目录
     */
    public static void saveObfuscatedFiles(Map<String, CompilationUnit> fileMap, boolean needCreateDir) {
        if (!needCreateDir) {
            saveObfuscatedFiles(fileMap);
        } else {
            fileMap.forEach((path, cu) -> {
                try {
                    //获取java文件的绝对路径
                    Path filePath = Paths.get(path);
                    // 获取java文件的上一层根路径
                    Path parentDir = filePath.getParent();
                    // 由于是有目标路径,所以如果不存在就创建
                    if (parentDir != null && !Files.exists(parentDir)) {
                        Files.createDirectories(parentDir);
                    }
                    Files.writeString(filePath, cu.toString());
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
        }
    }


    /**
     * 删除从startDir文件夹往上到stopDir文件夹的位置,左闭右开区间;<br>
     *
     * @param startDir:删除开始的文件夹路径
     * @param stopDir:            删除终止的文件夹路径
     */
    //eg:  删除p3到p1的空文件夹
    //  startDir=/Users/gxp/project/p1/p2/p3,  stopDir=/Users/gxp/project/
    //      目录层级:
    //          Users/gxp/project           结果        /Users/gxp/project/
    //                          /p1        =======>
    //                             /p2
    //                                /p3
    public static void deleteEmptyDirs(Path startDir, Path stopDir) throws IOException {
        Path current = startDir;
        while (current.startsWith(stopDir) && !current.equals(stopDir)) {
            try (DirectoryStream<Path> entries = Files.newDirectoryStream(current)) {
                if (!entries.iterator().hasNext()) {
                    Files.delete(current);
                    current = current.getParent();
                } else {
                    break;
                }
            }
        }
    }


    /**
     * 删除sourceRoot下指定packageName中所有的java文件
     * eg: 删除sourceRoot下p1包
     * String sourceRoot = "/Users/eobardgu/thai_project/action-live-mix-app-android/sdk/src/main/java";
     * deleteJavaFilesInPackage("com/pxt/living/p1",sourceRoot);
     *
     * @param packageName:指定包名称,eg: com.eobard.utils或者com/eobard/utils 两种写法都可以
     * @param sourceRoot:根路径
     */
    public static void deleteJavaFilesInPackage(String packageName, String sourceRoot) {
//        String packagePath = packageName.replace(".", File.separator);
//        Path filePath = Paths.get(anyFilePathInPackage).toAbsolutePath();
//
//        int index = filePath.toString().lastIndexOf(packagePath);
//        if (index == -1) {
//            throw new IllegalArgumentException("路径不匹配");
//        }
//        Path packageDir = Paths.get(filePath.toString().substring(0, index + packagePath.length()));
//
//        try (DirectoryStream<Path> stream = Files.newDirectoryStream(packageDir, "*.java")) {
//            for (Path entry : stream) {
//                Files.deleteIfExists(entry);
//            }
//        } catch (IOException e) {
//            throw new RuntimeException("删除失败", e);
//        }

        // 将包名转为目录形式
        String packagePath = packageName.replace(".", File.separator);
        Path packageDir = Paths.get(sourceRoot, packagePath).toAbsolutePath();

        if (!Files.exists(packageDir)) {
            return;
        }
        try {
            Files.walk(packageDir)
                    .filter(p -> p.toString().endsWith(".java"))
                    .forEach(p -> {
                        try {
                            Files.deleteIfExists(p);
                        } catch (IOException e) {
                            log.error("删除失败：{} -> {}", p, e.getMessage());
                        }
                    });
            deleteEmptyDirs(packageDir, Paths.get(sourceRoot).toAbsolutePath());
        } catch (IOException e) {
            throw new RuntimeException("遍历删除失败", e);
        }
    }

    /**
     * 检查dirs是否属于resourceRoot路径下
     *
     * @param resourceRoot
     * @param dirs
     */
    public static void checkDirectoryExist(Collection<String> dirs, String resourceRoot) {
        if (CollectionUtil.isEmpty(dirs)) throw new RuntimeException(dirs + "为空集合");
        //获取resourceRoot中所有的文件夹
        Set<String> rootSubDirs = Arrays.stream(FileUtil.ls(resourceRoot))
                .filter(File::isDirectory)
                .map(File::getName)
                .collect(Collectors.toSet());
        //判断如果传入的文件夹名称和已有的文件夹不符,则报错
        dirs.stream().filter(dir ->
                {
                    // 1.先检查原样目录名是否存在
                    if (rootSubDirs.contains(dir)) {
                        return false;
                    }
                    // 2.如果有目录名由.构成,转换.为/继续判断
                    String pathWithSlash = dir.replace(".", File.separator);
                    File dirWithSlash = new File(resourceRoot, pathWithSlash);
                    if (dirWithSlash.exists() && dirWithSlash.isDirectory()) {
                        return false;
                    }
                    // 3.如果有目录由.构成,转换.为/并且判断是否是子文件夹
                    String bareName = dir.substring(dir.lastIndexOf(".") + 1);
                    File bareDir = new File(resourceRoot, bareName);
                    if (bareDir.exists() && bareDir.isDirectory()) {
                        return false;
                    }
                    return true;// 不存在，需要抛出异常
                })
                .findFirst()
                .ifPresent(dir -> {
                    throw new RuntimeException(String.format(
                            "资源目录 【 %s 】不存在或为空（已尝试：【 /%s 】、【 /%s 】 和 【 /%s 】）",
                            dir, dir, dir.replace(".", File.separator), dir.substring(dir.lastIndexOf(".") + 1))
                    );
                });
    }

    /**
     * 检查dir文件夹下的文件是否是指定ext的类型
     *
     * @param dir
     * @param ext
     * @return
     */
    public static void checkFileExt(List<String> dir, List<String> ext) {
        if (CollectionUtil.isNotEmpty(dir)) {
            dir.forEach(d -> {
                if (!FileUtil.isDirectory(d)) {
                    throw new RuntimeException(d + " 路径不存在或不是一个文件夹");
                }
                if (FileUtil.loopFiles(d).stream()
                        .map(f -> f.getName().toLowerCase())//获取文件名称
                        .noneMatch(name -> ext.stream()
                                .anyMatch(suffix -> name.endsWith(suffix.toLowerCase())))) {
                    throw new RuntimeException(d + " 文件夹下不是以: " + ext + "结尾的文件");
                }
            });
        }
    }


    /**
     * 检查dir路径下是否是指定ext的类型
     *
     * @param dir
     * @param ext
     */
    public static void checkFileExt(String dir, List<String> ext) {
        if (StrUtil.isNotBlank(dir)) {
            if (!FileUtil.isDirectory(dir)) {
                throw new RuntimeException(dir + " 路径不存在或不是一个文件夹");
            }
            if (FileUtil.loopFiles(dir).stream()
                    .map(f -> f.getName().toLowerCase())//获取文件名称
                    .noneMatch(name -> ext.stream()
                            .anyMatch(suffix -> name.endsWith(suffix.toLowerCase())))) {
                throw new RuntimeException(dir + " 文件夹下不是以: " + ext + "结尾的文件");
            }
        }
    }

    /**
     * 检查文件是否是 RESOURCE_FILE_EXT 资源文件的类型
     *
     * @param file
     * @return
     */
    public static boolean isResourceFile(File file) {
        return Optional.ofNullable(file)
                .map(File::getName)
                .filter(extName -> RESOURCE_FILE_EXT.stream().anyMatch(extName::endsWith))
                .isPresent();
    }

    //将资源文件夹下的代码复制到临时文件夹
    public static File copyClasspathSourceToTempDir(String resourceDir) throws IOException {
        //删除上一次的临时文件目录
        File lastTimeFile = TempDirCleaner.getTempDir();
        if (ObjectUtil.isNotEmpty(lastTimeFile) && lastTimeFile.exists()) {
            log.info("deleting previous temp directory:{}", lastTimeFile.getAbsolutePath());
            FileUtils.deleteDirFileRecursively(lastTimeFile);
        }

        //创建临时文件
        File tempDir = Files.createTempDirectory("source_ext_").toFile();
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        //获取所有java文件
        Resource[] resources = resolver.getResources("classpath*:" + resourceDir + "/**/*.java");

        for (Resource resource : resources) {
            if (!resource.exists()) continue;

            // 1. 读取内容
            String content = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8);

            // 2. 提取 package 路径（正则找出 package cn.dev33.satoken.xxx;）
            Matcher matcher = Pattern.compile("package\\s+([a-zA-Z0-9_.]+);").matcher(content);
            if (!matcher.find()) {
                // 如果没有package就跳过该文件并提示
                log.error("skipping file {},it doesn't has the package declaration", resource.getURL().getPath().replaceFirst("^.*?/classes/", "resources:"));
                continue;
            }

            String packageName = matcher.group(1);
            String packagePath = packageName.replace('.', '/');

            // 3. 获取类名
            String filename = Objects.requireNonNull(resource.getFilename());

            // 4. 拼接到 tempDir 中
            File targetFile = new File(tempDir, packagePath + "/" + filename);
            targetFile.getParentFile().mkdirs();

            try (OutputStream out = new FileOutputStream(targetFile)) {
                out.write(content.getBytes(StandardCharsets.UTF_8));
            }
            //5.注入临时文件路径
            TempDirCleaner.setTempDir(tempDir);
        }
        return tempDir;
    }

    //删除指定文件及其子文件的所有
    public static void deleteDirFileRecursively(File file) {
        if (file.isDirectory()) {
            for (File child : Objects.requireNonNull(file.listFiles())) {
                deleteDirFileRecursively(child);
            }
        }
        file.delete();
    }
}
